package com.hbzhit.common.utils;

import cn.hutool.core.collection.CollUtil;
import com.hbzhit.common.exception.RRException;
import lombok.SneakyThrows;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 处理多层级树状结构数据工具类
 *
 * @author Tiger.Zhang
 * 2019/9/18 8:43
 **/
public class TreeUtils {

    /** 获取对象ID得方法名 **/
    private static final String KEY_FUNC_NAME = "getId";
    /** 获取对象Code的方法 **/
    private static final String CODE_FUNC_NAME = "getCode";
    /** 获取对象Name的方法 **/
    private static final String NAME_FUNC_NAME = "getName";
    /** 获取对象父级ID得方法名 **/
    private static final String PARENT_KEY_FUNC_NAME = "getParentId";
    /** 设置子数据集方法名 **/
    private static final String SET_CHILDREN_FUNC_NAME = "setChildren";

    public static <T> List<T> listToTree(Class<T> clazz, List<T> srcList) {
        return listToTreeHandle(clazz, srcList, null, null);
    }

    public static <T> List<Map<String, Object>> listToTreeOptions(Class<T> clazz, List<T> srcList) {
        return listToTreeOptionsHandle(clazz, srcList, null, null, true);
    }

    public static <T> List<Map<String, Object>> listToTreeOptions(Class<T> clazz, List<T> srcList, Boolean idValue) {
        return listToTreeOptionsHandle(clazz, srcList, null, null, idValue);
    }

    public static List<Map<String, Object>> MultiMapListToTree(List<Map<String, Object>> srcList) {
        return MultiMapListToTreeHandle(srcList, null, null);
    }

    /**
     * 处理含有层级关系的对象集合，处理为包含children的多层的对象集合数据
     *
     * @param clazz     实体对象class
     * @param srcList   对象列表
     * @param pKey      当前处理的上级对象Pk
     * @param usedKey   已经处理过对象PK,避免重复判断
     * @param <T>       对象泛型
     * @return          返回父级PK==pKey的对象列表
     */
    @SneakyThrows
    private static <T> List<T> listToTreeHandle(Class<T> clazz, List<T> srcList, Object pKey, List<Object> usedKey) {
        List<T> objList = new ArrayList<>();
        if (usedKey == null) {
            usedKey = new ArrayList<>();
        }
        Method pkm = getDeclaredMethod(clazz, PARENT_KEY_FUNC_NAME);
        Method km = getDeclaredMethod(clazz, KEY_FUNC_NAME);
        Method s = getDeclaredMethod(clazz, SET_CHILDREN_FUNC_NAME, List.class);
        if (pkm == null || km == null || s == null) {
            throw new RRException("树状表格数据处理错误，缺失相应方法");
        }

        for (T o : srcList) {
            // 对象PK
            Object ok = km.invoke(o);
            // 对象pk已在递归中使用过，跳过
            if (usedKey.contains(ok)) {
                continue;
            }
            // 对象父级PK
            Object opKey = pkm.invoke(o);

            // 当前调用上级ID为空并且此对象上级ID为空，即为处理顶层数据
            boolean isTop = pKey == null && opKey == null;
            // 当前调用上级ID有值且此对象上级ID等于当前调用的上级ID，即为处理当前调用上级ID的子数据
            boolean isSub = pKey != null && pKey.equals(opKey);
            // 以上两种情况处理当前返回的集合
            if (isTop || isSub) {
                usedKey.add(ok);
                List<T> subList = listToTreeHandle(clazz, srcList, ok, usedKey);
                s.invoke(o, subList);
                objList.add(o);
            }
        }
        return objList;
    }

    /**
     * 处理含有层级关系的对象集合，处理为包含children的多层的map集合数据
     *
     * @param clazz     实体对象class
     * @param srcList   对象列表
     * @param pKey      当前处理的上级对象Pk
     * @param usedKey   已经处理过对象PK,避免重复判断
     * @param idValue   是否使用id作为选型value值
     * @param <T>       对象泛型
     * @return          返回父级PK==pKey的对象列表
     */
    @SneakyThrows
    private static <T> List<Map<String, Object>> listToTreeOptionsHandle(Class<T> clazz,
                                                                         List<T> srcList,
                                                                         Object pKey,
                                                                         List<Object> usedKey,
                                                                         Boolean idValue) {
        List<Map<String, Object>> objList = new ArrayList<>();
        if (usedKey == null) {
            usedKey = new ArrayList<>();
        }
        Method pkm = getDeclaredMethod(clazz, PARENT_KEY_FUNC_NAME);
        Method km = getDeclaredMethod(clazz, KEY_FUNC_NAME);
        Method cm = getDeclaredMethod(clazz, CODE_FUNC_NAME);
        Method nm = getDeclaredMethod(clazz, NAME_FUNC_NAME);
        if (pkm == null || km == null || cm == null || nm == null) {
            throw new RRException("树状表格数据处理错误，缺失相应方法");
        }

        for (T o : srcList) {
            // 对象PK
            Object ok = km.invoke(o);
            // 对象pk已在递归中使用过，跳过
            if (usedKey.contains(ok)) {
                continue;
            }
            // 对象父级PK
            Object opKey = pkm.invoke(o);

            // 当前调用上级ID为空并且此对象上级ID为空，即为处理顶层数据
            boolean isTop = pKey == null && opKey == null;
            // 当前调用上级ID有值且此对象上级ID等于当前调用的上级ID，即为处理当前调用上级ID的子数据
            boolean isSub = pKey != null && pKey.equals(opKey);
            // 以上两种情况处理当前返回的集合
            if (isTop || isSub) {
                usedKey.add(ok);
                String oc = (String) cm.invoke(o);
                String on = (String) nm.invoke(o);
                Map<String, Object> oMap = new HashMap<>(5);
                // 可能存在多个不同类型的树同时处理，标记类型
                oMap.put("id", ok);
                oMap.put("code", oc);
                if (idValue) {
                    oMap.put("value", ok);
                } else {
                    oMap.put("value", oc);
                }
                oMap.put("label", on);
                oMap.put("parentId", pKey);
                List<Map<String, Object>> subList = listToTreeOptionsHandle(clazz, srcList, ok, usedKey, idValue);
                if (CollUtil.isNotEmpty(subList)) {
                    oMap.put("children", subList);
                } else {
                    oMap.put("leaf", true);
                }
                objList.add(oMap);
            }
        }
        return objList;
    }

    /**
     * 多种Map对象集合合并并转换为多层树结构
     *
     * @param srcList   对象列表
     * @param pKey      当前处理的上级对象pk
     */
    private static List<Map<String, Object>> MultiMapListToTreeHandle(List<Map<String, Object>> srcList,
                                                               Object pKey,
                                                               List<Object> usedKeys) {
        List<Map<String, Object>> resList = new ArrayList<>();
        if (usedKeys == null) usedKeys = new ArrayList<>();
        for (Map<String, Object> o : srcList) {
            // 对象key
            Object oKey = o.get("key");
            // 对象父级key
            Object oPKey = o.get("pKey");
            // 当前为顶级数据或当前对象的父级key=传入的需要处理的父级key
            if ((oPKey == null && pKey == null) || (pKey != null && pKey.equals(oPKey))) {
                usedKeys.add(oKey);
                List<Map<String, Object>> subList = MultiMapListToTreeHandle(srcList, oKey, usedKeys);
                if (CollUtil.isNotEmpty(subList)) {
                    o.put("children", subList);
                } else {
                    o.put("leaf", true);
                }
                resList.add(o);
            }
        }
        return resList;
    }

    /**
     * 循环向上转型, 获     * @param object : 子类对象
     * @param methodName : 父类中的方法名
     * @param parameterTypes : 父类中的方法参数类型
     * @return 父类中的方法对象
     */

    private static Method getDeclaredMethod(Class<?> clazz, String methodName, Class<?> ... parameterTypes) {
        Method method = null ;

        while (clazz != Object.class) {
            try {
                method = clazz.getDeclaredMethod(methodName, parameterTypes);
                return method;
            } catch (Exception e) {
                clazz = clazz.getSuperclass();
            }
        }

        return null;
    }
}
